/*************************************************
 * ThinkJot V2
 * Author: Jeswin P. (jeswin@process64.com)
 * Website: http://www.process64.com/thinkjot/
 * This code is distributed under the terms of the
 * Apache License, Version 2.0.
 * **********************************************/
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

using ThinkJot.Core.Configuration;
using ThinkJot.Core.Utils;

namespace ThinkJot.Core.Blogs.Providers.Xml
{
    public class XmlBlogEntryDP : BlogEntryDP
    {
        public override BlogEntry GetBlogEntry(Guid blogEntryId, string blogName)
        {
            try
            {
                string file = Utils.GetEntryFilePath(blogEntryId, blogName);
                BlogEntry entry = (BlogEntry)new SerializationHelper().Deserialize(file, typeof(BlogEntry), ProviderSettings.NamespaceURI);
                if (entry == null) return null;
                List<Comment> comments = new XmlCommentDP().GetComments(blogEntryId, blogName);
                entry.Comments = comments;
                return entry;
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                return null;
            }
        }

        public override BlogEntry GetDeletedBlogEntry(Guid blogEntryId, string blogName)
        {
            try
            {
                string file = Utils.GetDeletedEntryFilePath(blogEntryId, blogName);
                return (BlogEntry)new SerializationHelper().Deserialize(file, typeof(BlogEntry), ProviderSettings.NamespaceURI);
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                return null;
            }
        }

        /// <summary>
        /// This creates a new blog entry
        /// </summary>
        /// <param name="entry"></param>
        /// <param name="blogName"></param>
        public override void SaveBlogEntry(BlogEntry entry, string blogName)
        {
            try
            {
                //see if the entry exists, do not overwrite!
                if (BlogEntryExists(entry.UniqueId, blogName))
                    return;

                //We touch a lot of files
                //Create (1) <blog_entry_uniqueid>.xml
                SerializationHelper serializer = new SerializationHelper();
                serializer.Serialize(entry, Utils.GetEntryFilePath(entry.UniqueId, blogName), ProviderSettings.NamespaceURI);

                //Add link to (1) to TitleMap.
                TitleMap titleMap = TitleMap.GetTitleMap(blogName);
                titleMap.AddEntry(entry);
                titleMap.Save(blogName);

                DateTimeMap dateTimeMap = DateTimeMap.GetDateTimeMap(blogName);
                dateTimeMap.AddEntry(entry);
                dateTimeMap.Save(blogName);

                BlogEntryTags.SetTagsToEntry(entry.Tags, entry.UniqueId, blogName);
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
            }
        }

        public override void UpdateBlogEntry(BlogEntry entry, string blogName)
        {
            try
            {
                SerializationHelper serializer = new SerializationHelper();
                serializer.Serialize(entry, Utils.GetEntryFilePath(entry.UniqueId, blogName), ProviderSettings.NamespaceURI);

                //Add link to (1) to TitleMap.
                TitleMap titleMap = TitleMap.GetTitleMap(blogName);
                titleMap.Remove(entry.UniqueId);
                titleMap.AddEntry(entry);
                titleMap.Save(blogName);

                //Add link to (1) to DateTimeMap.
                DateTimeMap dateTimeMap = DateTimeMap.GetDateTimeMap(blogName);
                dateTimeMap.Remove(entry.UniqueId);
                dateTimeMap.AddEntry(entry);
                dateTimeMap.Save(blogName);

                BlogEntryTags.SetTagsToEntry(entry.Tags, entry.UniqueId, blogName);
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
            }
        }

        public override void DeleteBlogEntry(Guid entryId, string blogName)
        {
            try
            {
                BlogEntry entry = GetBlogEntry(entryId, blogName);

                //move the file to deleted folder and delete the original.
                string fileName = Utils.GetEntryFileName(entryId);
                string entryFilePath = Utils.GetEntryFilePath(entryId, blogName);
                string entryTrashPath = Path.Combine(ProviderSettings.GetDeletedPostsFolder(blogName), fileName);

                if (File.Exists(entryFilePath))
                {
                    File.Copy(entryFilePath, entryTrashPath, true);

                    fileName = Utils.GetCommentFileName(entryId);
                    string commentFilePath = Utils.GetCommentFilePath(entryId, blogName);
                    string commentTrashPath = Path.Combine(ProviderSettings.GetDeletedPostsFolder(blogName), fileName);
                    //comments file may not exist if there are no comments.
                    if (File.Exists(commentFilePath))
                        File.Copy(commentFilePath, commentTrashPath, true);

                    File.Delete(entryFilePath);
                    File.Delete(commentFilePath);

                    SerializationHelper serializer = new SerializationHelper();
                    //Remove from TitleMap            
                    TitleMap titleMap = TitleMap.GetTitleMap(blogName);
                    titleMap.Remove(entryId);
                    titleMap.Save(blogName);

                    //Remove from DateTimeMap   
                    DateTimeMap dateTimeMap = DateTimeMap.GetDateTimeMap(blogName);
                    dateTimeMap.Remove(entryId);
                    dateTimeMap.Save(blogName);

                    BlogEntryTags.RemoveEntry(entryId, blogName);
                }
                SerializationManager.RemoveObjectFromCache(entryFilePath);
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
            }
        }

        //? TODO: Optimize.
        //This is super heavy!
        //Use Lucene.Net?
        public override List<BlogEntry> SearchBlogEntries(string blogName, string[] searchStrings)
        {
            try
            {
                if (searchStrings.Length == 0) return new List<BlogEntry>();

                BlogConfig blogConfig = SystemConfiguration.GetConfig().Blogs.GetBlogConfig(blogName);
                DateTimeMap dateTimeMap = DateTimeMap.GetDateTimeMap(blogName);

                List<BlogEntry> matchingEntries = new List<BlogEntry>();
                for (int i = (dateTimeMap.Entries.Count - 1); i >= 0; i--)
                {
                    DateTimeMapEntry dtEntry = dateTimeMap.Entries[i];
                    BlogEntry entry = GetBlogEntry(dtEntry.EntryId, blogName);

                    foreach (string keyword in searchStrings)
                    {
                        if (string.IsNullOrEmpty(keyword))
                            continue;

                        string searchString = keyword.ToLower();

                        if (entry.Title.ToLower().Contains(searchString) ||
                            entry.Description.ToLower().Contains(searchString) ||
                            entry.Content.ToLower().Contains(searchString))
                        {
                            matchingEntries.Add(entry);
                            break;
                        }
                    }
                    if (matchingEntries.Count == blogConfig.MaxSearchResults)
                        break;
                }
                matchingEntries.Sort();
                matchingEntries.Reverse();
                return matchingEntries;
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                return new List<BlogEntry>();
            }
        }

        /// <summary>
        /// Probably the most expensive operation. Refine, but how?
        /// </summary>
        /// <returns></returns>
        public override List<BlogEntry> GetAllBlogEntries(string blogName)
        {
            try
            {
                string[] files = Directory.GetFiles(ProviderSettings.GetBlogContentFolder(blogName), "*.entry.xml");
                List<BlogEntry> entries = new List<BlogEntry>();
                foreach (string file in files)
                {
                    entries.Add((BlogEntry)new SerializationHelper().Deserialize(file, typeof(BlogEntry), ProviderSettings.NamespaceURI));
                }
                return entries;
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                return new List<BlogEntry>();
            }
        }

        public override List<BlogEntry> GetBlogEntries(List<Guid> entryIds, string blogName)
        {
            try
            {
                List<BlogEntry> entries = new List<BlogEntry>();
                foreach (Guid entryId in entryIds)
                {
                    BlogEntry entry = GetBlogEntry(entryId, blogName);
                    if (entry != null) entries.Add(entry);
                }
                return entries;
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                return new List<BlogEntry>();
            }
        }

        public override List<BlogEntry> GetAllBlogEntriesAfter(DateTime when, string blogName)
        {
            try
            {
                SerializationHelper serializer = new SerializationHelper();
                List<BlogEntry> results = new List<BlogEntry>();

                DateTimeMap dateTimeMap = DateTimeMap.GetDateTimeMap(blogName);

                List<Guid> entryIds = dateTimeMap.GetEntryIdsLaterThan(when);
                List<BlogEntry> entries = GetBlogEntries(entryIds, blogName);
                entries.Reverse();
                return entries;
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                return new List<BlogEntry>();
            }
        }

        public override List<BlogEntry> GetLastNormalTypeEntries(int count, bool showUnpublished, string blogName)
        {
            try
            {
                SerializationHelper serializer = new SerializationHelper();
                List<BlogEntry> results = new List<BlogEntry>();

                DateTimeMap dateTimeMap = DateTimeMap.GetDateTimeMap(blogName);
                if (dateTimeMap == null) return new List<BlogEntry>();

                List<BlogEntry> entries = new List<BlogEntry>();
                List<Guid> entryIds = dateTimeMap.GetLastEntryIds(count, showUnpublished, false);

                foreach (Guid entryId in entryIds)
                {
                    BlogEntry entry = GetBlogEntry(entryId, blogName);
                    if (entry != null) entries.Add(entry);
                }
                return entries;
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                return new List<BlogEntry>();
            }
        }

        public override string SaveFile(string fileName, byte[] fileBytes, string blogName, bool overwrite)
        {
            try
            {
                string mediaFolder = new PathUtility().GetBlogMediaFolder(blogName);
                string filePath = Path.Combine(mediaFolder, fileName);
                string fileName_without_ext = Path.GetFileNameWithoutExtension(filePath);
                string extension = Path.GetExtension(filePath);

                if (!overwrite)
                {
                    int counter = 1;
                    while (File.Exists(filePath))
                    {
                        string newFileName = fileName_without_ext + counter.ToString() + extension;
                        filePath = Path.Combine(mediaFolder, newFileName);
                        counter++;
                    }
                }
                // Create the file.
                using (System.IO.FileStream fs = System.IO.File.Create(filePath, 1024))
                {
                    // Add some information to the file.
                    fs.Write(fileBytes, 0, fileBytes.Length);
                }
                return Path.GetFileName(filePath);
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                return null;
            }
        }

        public override BlogEntry GetBlogEntryByTitle(string title, string blogName)
        {
            try
            {
                TitleMap titleMap = TitleMap.GetTitleMap(blogName);
                Guid entryId = titleMap.GetEntryId(title);
                return GetBlogEntry(entryId, blogName);
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                return new BlogEntry();
            }
        }

        public override List<BlogEntry> GetEntriesForMonth(int month, int year, string blogName)
        {
            try
            {
                DateTimeMap map = DateTimeMap.GetDateTimeMap(blogName);
                List<BlogEntry> results = GetBlogEntries(map.GetEntryIdsForMonth(month, year), blogName);
                results.Reverse();
                return results;
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                return new List<BlogEntry>();
            }
        }

        public override List<BlogEntry> GetEntriesInCategory(int categoryId, int start, int count, out int totalItems, string blogName)
        {
            try
            {
                List<BlogEntry> allEntries = GetAllBlogEntries(blogName);
                List<BlogEntry> matchingEntries = new List<BlogEntry>();
                foreach (BlogEntry entry in allEntries)
                {
                    if (entry.CategoryIds.Contains(categoryId)) matchingEntries.Add(entry);
                }
                matchingEntries.Sort();
                matchingEntries.Reverse();
                totalItems = matchingEntries.Count;

                if (start >= totalItems)
                    return new List<BlogEntry>();
                else if ((start + count) > totalItems)
                    count = totalItems - start;

                return matchingEntries.GetRange(start, count);
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                totalItems = 0;
                return new List<BlogEntry>();
            }
        }

        public override List<BlogEntry> GetEntriesByTag(string tag, int start, int count, out int totalItems, string blogName)
        {
            try
            {
                List<Guid> matchingIds = BlogEntryTags.GetEntriesByTag(tag, blogName);
                List<BlogEntry> entries = new List<BlogEntry>();
                foreach (Guid id in matchingIds)
                    entries.Add(GetBlogEntry(id, blogName));
                entries.Sort();
                entries.Reverse();
                totalItems = entries.Count;

                if (start >= totalItems)
                    return new List<BlogEntry>();
                else if ((start + count) > totalItems)
                    count = totalItems - start;

                return entries.GetRange(start, count);
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                totalItems = 0;
                return new List<BlogEntry>();
            }
        }

        public override List<BlogEntrySummary> GetLastEntriesSummary(int count, string blogName)
        {
            try
            {
                List<BlogEntrySummary> results = new List<BlogEntrySummary>();
                DateTimeMap dateTimeMap = DateTimeMap.GetDateTimeMap(blogName);
                List<DateTimeMapEntry> entries = dateTimeMap.GetLastEntries(count);
                BlogEntrySummary summary;
                foreach (DateTimeMapEntry entry in entries)
                {
                    summary = new BlogEntrySummary(entry.Time, entry.EntryId, entry.Title, entry.Author, entry.Publish);
                    results.Add(summary);
                }
                results.Reverse();
                return results;
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                return new List<BlogEntrySummary>();
            }
        }

        public override List<BlogEntry> GetDeletedEntries(string blogName)
        {
            try
            {
                List<BlogEntry> results = new List<BlogEntry>();
                string trashPath = ProviderSettings.GetDeletedPostsFolder(blogName);
                string[] files = Directory.GetFiles(trashPath, "*.entry.xml");

                foreach (string file in files)
                {
                    BlogEntry entry = (BlogEntry)new SerializationHelper().Deserialize(file, typeof(BlogEntry), ProviderSettings.NamespaceURI);
                    results.Add(entry);
                }
                results.Reverse();
                return results;
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                return new List<BlogEntry>();
            }
        }

        public override void Undelete(Guid entryId, string blogName)
        {
            try
            {
                string entryFilePath = Utils.GetDeletedEntryFilePath(entryId, blogName);
                string commentFilePath = Utils.GetDeletedCommentFilePath(entryId, blogName);
                if (File.Exists(entryFilePath))
                {
                    BlogEntry entry = (BlogEntry)new SerializationHelper().Deserialize(entryFilePath, typeof(BlogEntry), ProviderSettings.NamespaceURI);
                    SaveBlogEntry(entry, blogName);
                    File.Delete(entryFilePath);
                    //We just need to copy the comments file to the content folder.
                    if (File.Exists(commentFilePath))
                    {
                        string commentDestPath = Utils.GetCommentFilePath(entryId, blogName);
                        File.Copy(commentFilePath, commentDestPath, true);
                        File.Delete(commentFilePath);
                    }
                }
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
            }
        }

        public override Guid GetEntryIdForTitle(string title, string blogName)
        {
            try
            {
                TitleMap titleMap = TitleMap.GetTitleMap(blogName);
                return titleMap.GetEntryId(title);
            }
            catch (Exception ex)
            {
                new XmlLogger().LogException(ex, blogName);
                return Guid.Empty;
            }
        }

        private bool BlogEntryExists(Guid entryId, string blogName)
        {
            string entryFilePath = Utils.GetEntryFilePath(entryId, blogName);
            return File.Exists(entryFilePath);
        }
    }
}